fix: use cumulative prefix sets for incremental trie state root#445
fix: use cumulative prefix sets for incremental trie state root#445
Conversation
b74a166 to
baa5064
Compare
docs/TRIE_CACHE_BENCHMARK_REPORT.md
Outdated
There was a problem hiding this comment.
We can keep the bench but maybe not the result?
If we choose to keep the result, atleast move it in crates/op-builder/benches/docs or results.
There was a problem hiding this comment.
I will move it to the benches folder, want a template for what the benchmark reports should look like
There was a problem hiding this comment.
Maybe add a template then instead of an example?
akundaz
left a comment
There was a problem hiding this comment.
The benchmark and tests run against replicated state root calculations instead of the actual production code path. Can you extract the code to run this calculation and and update the benchmark and tests to call that directly? Otherwise we can't really draw conclusions about real world performance from these results. And if the code drifts we should be able to catch with our tests and the benchmark.
The incremental trie cache produces wrong state roots when a storage slot modified in flashblock N reverts in flashblock N+1. The reverted slot disappears from the cumulative HashedPostState, so its nibble path is missing from the prefix set. The trie walker skips the subtree and uses the stale cached hash from the previous flashblock's branch node. Fix: track cumulative TriePrefixSetsMut across all flashblocks. Before building TrieInput, extend the current prefix sets with all prior flashblocks' prefix sets. This forces the walker to re-visit every path modified in earlier flashblocks. The fix is also ~30% faster than the unfixed incremental path because descending into cached in-memory nodes is faster than DB cursor seeks for skipped branches. Benchmarks (10 flashblocks, 50k accounts): - Without cache: ~2,200ms (baseline) - Incremental (no fix): ~844ms (2.6x faster, incorrect on reverts) - Incremental (with fix): ~650ms (3.4x faster, correct) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the incremental state root calculation logic from build_flashblock_payload_inner into builder::state_root::compute_state_root. Tests and benchmarks now call the same function as production code, ensuring they exercise the actual code path rather than replicated logic that could drift. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c866328 to
56da125
Compare
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The I would also like to see better encapsulation of the state root computation. Right now it's spread across state_root.rs and payload.rs but we should try to get it all in state_root.rs (the unit test should go there as well). I would suggest that instead of |
This change was just illustrative to prove that the issue is reproducible and that the new code fixes the issue. I can remove it if you're satisfied this fix works with reverts. |
That's fine! Just make sure to have a test with reverts since it triggers the bug |
Fix: track cumulative TriePrefixSetsMut across all flashblocks. Before building TrieInput, extend the current prefix sets with all prior flashblocks' prefix sets. This forces the walker to re-visit every path modified in earlier flashblocks.
The fix is also ~30% faster than the unfixed incremental path because descending into cached in-memory nodes is faster than DB cursor seeks for skipped branches.
Benchmarks (10 flashblocks, 50k accounts):